package edu.hawaii.senin.iclouds.basics;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.junit.Before;
import org.junit.Test;

/**
 * Tests normal Molecule operations.
 * 
 * @author Pavel Senin.
 * 
 */
public class TestMolecule {

  /**
   * test Molecule1.
   */
  private Molecule molecule1;
  private static final String name1 = "H";
  private static final Double amu1 = 1.008D;
  private static final Double abundance1 = Double.valueOf(0.33D);
  private static final Double fraction1 = null;
  private static final String fractionOf1 = null;

  /**
   * test Molecule2.
   */
  private Molecule molecule2;
  private static final String name2 = "D";
  private static final Double amu2 = 2.04D;
  private static final Double abundance2 = Double.valueOf(0D);
  private static final Double fraction2 = Double.valueOf(1.0E-3D);
  private static final String fractionOf2 = "H";

  private static final String fileName1 = "config/test_cfg/testIO01.xml";

  /**
   * Little test fixture.
   */
  @Before
  public void runBeforeEachTest() {
    // public Molecule(String name, Double amu, Double abundance, Double fraction, String frOf,
    // Double accretionProbability, Double velocity, String type, Long accumulator) {
    molecule1 = new Molecule(name1, amu1, abundance1, fraction1, fractionOf1, 0D, 0D, "active", 0L);
    molecule2 = new Molecule(name2, amu2, abundance2, fraction2, fractionOf2, 0D, 
          0D, "passive", 0L);
    // population.add(molecule1.getName(), molecule1, amount)
  }

  /**
   * Tests getters and setters.
   * 
   */
  @Test
  public void testSetAndGetMethods() {
    // private final String name1 = "H";
    // private final Double amu1 = 1.008D;
    // private final Double abundance1 = Double.valueOf(0.33D);
    // private final Double fraction1 = Double.valueOf(0D);
    // private final String fractionOf1 = "";
    assertEquals("Testing name getter", "H", molecule1.getName());
    assertEquals("Testing amu getter", 1.008D, molecule1.getAmu());
    assertEquals("Testing amu getter", 0.33D, molecule1.getAbundance());
    assertNull("Testing amu getter", molecule1.getFraction());
    assertNull("Testing amu getter", molecule1.getFractionOf());
    molecule1.setAccretionProbability(0.22D);
    assertEquals("Testing accretion probability getter", 0.22D, 
          molecule1.getAccretionProbability());
    molecule1.setVelocity(0.11D);
    assertEquals("Testing velocity getter", 0.11D, molecule1.getVelocity());
    assertTrue("Testing isActive() method ", molecule1.isActive());
    assertFalse("Testing isActive() method ", molecule2.isActive());

    molecule1.setVelocity(0.5D);
    Double rate1 = molecule1.getAbundance() * molecule1.getVelocity() * Math.PI * 0.5D;
    assertEquals("Testing getRate", rate1, molecule1.getRate(0.5D));
  }

  /**
   * Tests comparisons.
   * 
   */
  @Test
  public void testHashesAndCompares() {
    assertTrue("Testing comparison operations", molecule2.compareTo(molecule1) < 0);
    assertTrue("Testing comparison operations", molecule1.compareTo(molecule2) > 0);
    assertFalse("Testing comparison operations", molecule1.equals(molecule2));
    assertFalse("Testing comparison operations", molecule1.equals("zhaba"));
    assertTrue("Testing hash code operations", molecule1.hashCode() == molecule1.hashCode());
    assertFalse("Testing hash code operations", molecule1.hashCode() == molecule2.hashCode());

    Molecule molecule3 = molecule1.clone();
    assertEquals("Testing comparison operations", molecule1, molecule3);
  }

  /**
   * Tests accumulator operations.
   * 
   */
  @Test
  public void testAccumulator() {
    molecule1.setAccumulatorValue(10L);
    molecule1.incrementAccumulatorValue(1L);
    assertEquals("Testing accumulator operations", 11L, molecule1.getAccumulatorValue());
    molecule1.incrementAccumulatorValue(9L);
    assertEquals("Testing accumulator operations", 20L, molecule1.getAccumulatorValue());
    molecule1.decrementAccumulatorValue(9L);
    assertEquals("Testing accumulator operations", 11L, molecule1.getAccumulatorValue());
    molecule1.decrementAccumulatorValue(1L);
    assertEquals("Testing accumulator operations", 10L, molecule1.getAccumulatorValue());
  }

  /**
   * Tests IO rutines.
   * 
   */
  @Test
  public void testIOStuff() {
    molecule1.setAccumulatorValue(10L);
    molecule1.setAccretionProbability(0.8D);
    String shouldBe = "H: amu 1.008, abundance 0.33, type: active, vel: 0.0, accum: 10\n";
    // System.out.println(molecule1.toText());
    assertTrue("Testing toText", shouldBe.equalsIgnoreCase(molecule1.toText()));

    shouldBe = "D: amu 2.04, abundance 0.0, fr 0.0010, type: passive, vel: 0.0, accum: 0\n";
    // System.out.println(molecule2.toText());
    assertTrue("Testing toText", shouldBe.equalsIgnoreCase(molecule2.toText()));

    try {
      BufferedWriter writer = new BufferedWriter(new FileWriter(new File(TestMolecule.fileName1)));
      writer.write("<population>");
      molecule1.dump2XML(writer);
      molecule2.dump2XML(writer);
      writer.write("</population>");
      writer.flush();
      writer.close();

      SAXBuilder builder = new SAXBuilder();
      BufferedReader reader = new BufferedReader(new FileReader(new File(TestMolecule.fileName1)));
      Document popDoc = builder.build(reader);
      Element root = popDoc.getRootElement();
      if ((null == root) || (!root.getName().equals("population"))) {
        fail("got no or bad root element");
      }
      List molecules = root.getChildren();

      Molecule molecule3 = new Molecule((Element) molecules.get(0));
      assertEquals("Testing IO operations", "H", molecule3.getName());
      assertEquals("Testing IO operations", 1.008D, molecule3.getAmu());
      assertEquals("Testing IO operations", 0.33D, molecule3.getAbundance());
      assertNull("Testing IO operations", molecule3.getFraction());
      assertNull("Testing IO operations", molecule3.getFractionOf());
      assertTrue("Testing IO operations", molecule3.isActive());

      Molecule molecule4 = new Molecule((Element) molecules.get(1));
      // <molecule>
      // <name>D</name>
      // <amu>2.04</amu>
      // <abundance>0.0</abundance>
      // <fraction>0.0010</fraction>
      // <fraction_of>H</fraction_of>
      // <type>passive</type>
      // </molecule>
      assertEquals("Testing IO operations", "D", molecule4.getName());
      assertEquals("Testing IO operations", 2.04D, molecule4.getAmu());
      assertEquals("Testing IO operations", 0.0D, molecule4.getAbundance());
      assertEquals("Testing IO operations", 0.001D, molecule4.getFraction());
      assertEquals("Testing IO operations", "H", molecule4.getFractionOf());
      assertFalse("Testing IO operations", molecule4.isActive());

      reader.close();
    }
    catch (IOException e) {
      fail("Got IO exception " + e.getMessage());
    }
    catch (JDOMException e) {
      fail("Got JDOM exception " + e.getMessage());
    }
  }

}
